package cn.tml.innermost.music.service.impl;

import cn.tml.innermost.framework.entity.enums.ResultCode;
import cn.tml.innermost.framework.exception.ServiceException;
import cn.tml.innermost.framework.utils.StringUtils;
import cn.tml.innermost.music.dos.*;
import cn.tml.innermost.music.params.MusicSearchParams;
import cn.tml.innermost.music.params.SingerParams;
import cn.tml.innermost.music.params.SingerSearchParams;
import cn.tml.innermost.music.vo.*;
import cn.tml.innermost.music.mapper.MusicMapper;
import cn.tml.innermost.music.mapper.SingerMusicMapper;
import cn.tml.innermost.music.mapper.SingerAlbumMapper;
import cn.tml.innermost.music.mapper.SingerMapper;
import cn.tml.innermost.music.service.SingerService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 燧枫
 * @since 2022-10-18
 */
@Service
public class SingerServiceImpl extends ServiceImpl<SingerMapper, Singer> implements SingerService {
    @Resource
    SingerMusicMapper singerMusicMapper;
    @Resource
    SingerAlbumMapper singerAlbumMapper;
    @Resource
    MusicMapper musicMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    // 歌手的redsi前缀
    private static final String MUSIC_SINGER = "music-singer:";

    @Override
    public List<SingerInfoVO> search(MusicSearchParams musicSearchParams) {
        int startPage = musicSearchParams.getPageIndex();
        int resultNums = musicSearchParams.getResultNums();
        String keyWords = musicSearchParams.getKeyWords();
        Page<Singer> page = new Page<>(startPage, resultNums);
        this.getBaseMapper().selectPage(page, new QueryWrapper<Singer>().like(StringUtils.isNotEmpty(keyWords), "name", keyWords));
        List<SingerInfoVO> res = new ArrayList<>();
        List<Singer> records = page.getRecords();
        for (Singer singer : records) {
            res.add(SingerInfoVO.valueOf(singer));
        }
        return res;
    }

    /**
     * 获取歌手信息
     *
     * @param singerId
     * @return MusicListInfoVO
     */
    @Override
    public SingerInfoVO getSingerInfo(Long singerId) {
        Singer singer = null;
//        先判断是否在redis中
        if (redisTemplate.hasKey(MUSIC_SINGER + singerId)) {
//        如果在redis中, 直接得到歌手实体类
            singer = (Singer) redisTemplate.opsForValue().get(MUSIC_SINGER + singerId);
        } else {
//        不在, 根据歌曲id得到歌曲实体类
            singer = this.getById(singerId);
//        如果查不到，直接抛异常
            if (singer == null)
                throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        如果删除标记为true，直接抛异常
            if (singer.getDeleteFlag() == true)
                throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        保存至redis中
            redisTemplate.opsForValue().set(MUSIC_SINGER + singerId, singer, 60 * 60, TimeUnit.SECONDS);
        }
//        如果有，转为ingerInfoVO并返回
        return SingerInfoVO.valueOf(singer);
    }

    // 快速建立歌手与歌曲的关系表
    public void creatSingerAndMusic(Long current, Long size) {
        Page<Singer> page = new Page<>(current, size);
        this.getBaseMapper().selectPage(page, new QueryWrapper<Singer>().eq("delete_flag", 0));
        for (int i = 0; i < page.getRecords().size(); i++) {
            Singer singer = page.getRecords().get(i);
            Map<String, Object> columnMap = new HashMap<>();
            columnMap.put("singer_name", singer.getName());
            List<Music> list = musicMapper.selectByMap(columnMap);
            singer.setMusicNums(list.size());
            updateById(singer);
            for (Music music : list) {
                SingerMusic singerMusic = new SingerMusic(singer.getId(), music.getId(), music.getName(), singer.getName(), music.getDuration(), music.getCoverUrl());
                singerMusicMapper.insert(singerMusic);
            }
        }
    }

    /***
     * 分页获取所有歌手
     * @param 当前页码 current
     * @param 每页条数 size
     * @return: SingerPageVO
     */
    public SingerPageVO getAllSingerInfo(Long current, Long size) {
//        限制一波分页大小（20）
        if (size > 20)
            throw new ServiceException(ResultCode.PAGE_TOO_BIG);
        SingerPageVO singerPageVO = null;
        List<Long> singerList = new ArrayList<>();
        List<SingerInfoVO> singerInfoVOList = new ArrayList<>();
//        先判断是否在redis中
        if (redisTemplate.hasKey(MUSIC_SINGER + current + "&" + size)) {
            singerList = redisTemplate.opsForList().
                    range(MUSIC_SINGER + current + "&" + size, 0, -1);
//        通过list取出redis中所有实体类
            for (int i = 0; i < singerList.size(); i++) {
//        判断实体类是否在redis中
                if (redisTemplate.hasKey(MUSIC_SINGER + singerList.get(i))) {
                    singerInfoVOList.add(SingerInfoVO.valueOf((Singer) redisTemplate.opsForValue()
                            .get(MUSIC_SINGER + singerList.get(i))));
                }
            }
//        再取出PageVO,并封装成musicPageVO
            if (redisTemplate.hasKey(MUSIC_SINGER + current + "&" + size + "pageOV")) {
                singerPageVO = new SingerPageVO((PageOV) redisTemplate.opsForValue()
                        .get(MUSIC_SINGER + current + "&" + size + "pageOV"), singerInfoVOList);
            }
//        不在redis中
        } else {
//        根据current与size直接查询
            Page<Singer> page = new Page<>(current, size);
            this.getBaseMapper().selectPage(page, new QueryWrapper<Singer>().eq("delete_flag", 0));
//        提取PageOV所需信息并封装
            PageOV pageOV = new PageOV();
            pageOV.setCurrent(current);
            pageOV.setSize(size);
            pageOV.setTotal(page.getTotal());
            pageOV.setPages(page.getPages());
//        将pageOV保存到redis中
            redisTemplate.opsForValue().
                    set(MUSIC_SINGER + current + "&" + size + "pageOV", pageOV, 60 * 60, TimeUnit.SECONDS);
//        将提取的所有Singer实体类转换成SingerInfoVO并封装到List中
            for (int i = 0; i < page.getRecords().size(); i++) {
                Singer singer = page.getRecords().get(i);
                singerList.add(singer.getId());
                redisTemplate.opsForValue().
                        set(MUSIC_SINGER + singer.getId(), singer, 60 * 60, TimeUnit.SECONDS);
                singerInfoVOList.add(SingerInfoVO.valueOf(singer));
            }
            singerPageVO = new SingerPageVO(pageOV, singerInfoVOList);
//        保存至redis中
            redisTemplate.opsForList().rightPushAll(MUSIC_SINGER + current + "&" + size, singerList);
            redisTemplate.expire(MUSIC_SINGER + current + "&" + size, 60 * 60, TimeUnit.SECONDS);
        }
        return singerPageVO;
    }

    /***
     * @description: 分页获取歌手所有歌曲
     * @param singerId
     * @return: SingerPageVO
     */
    @Override
    public SingerMusicVO getSingerAllMusic(Long singerId, Long current, Long size) {
//        限制一波分页大小（100）
        if (size > 100)
            throw new ServiceException(ResultCode.PAGE_TOO_BIG);
        SingerMusicVO singerMusicVO = null;
//        根据标签id得到歌单实体类
        Singer singer = this.getById(singerId);
//        如果查不到，直接抛异常
        if (singer == null)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        如果删除标记为true，直接抛异常
        if (singer.getDeleteFlag() == true)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        根据current与size直接查询
        Page<SingerMusic> page = new Page<>(current, size);
        singerMusicMapper.selectPage(page, new QueryWrapper<SingerMusic>().eq("singer_id", singerId));
//        提取PageOV所需信息并封装
        //        提取PageOV所需信息并封装
        PageOV pageOV = new PageOV();
        pageOV.setCurrent(current);
        pageOV.setSize(size);
        pageOV.setTotal(page.getTotal());
        pageOV.setPages(page.getPages());
        singerMusicVO = new SingerMusicVO(pageOV, SingerInfoVO.valueOf(singer), page.getRecords());
        return singerMusicVO;
    }

    /***
     *  分页获取歌手所有专辑
     * @param singerId
     * @return SingerAlbumVO
     */
    @Override
    public SingerAlbumVO getSingerAllAlbum(Long singerId, Long current, Long size) {
//        限制一波分页大小（100）
        if (size > 100)
            throw new ServiceException(ResultCode.PAGE_TOO_BIG);
        SingerAlbumVO singerAlbumVO = null;
//        根据标签id得到歌单实体类
        Singer singer = this.getById(singerId);
//        如果查不到，直接抛异常
        if (singer == null)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        如果删除标记为true，直接抛异常
        if (singer.getDeleteFlag() == true)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        根据current与size直接查询
        Page<SingerAlbum> page = new Page<>(current, size);
        singerAlbumMapper.selectPage(page, new QueryWrapper<SingerAlbum>().eq("singer_id", singerId));
//        提取PageOV所需信息并封装
        //        提取PageOV所需信息并封装
        PageOV pageOV = new PageOV();
        pageOV.setCurrent(current);
        pageOV.setSize(size);
        pageOV.setTotal(page.getTotal());
        pageOV.setPages(page.getPages());
        singerAlbumVO = new SingerAlbumVO(pageOV, SingerInfoVO.valueOf(singer), page.getRecords());
        return singerAlbumVO;
    }

    /***
     *  新建一个歌手
     * @param singerParams
     * @return: SingerInfoVO
     */
    @Override
    public SingerInfoVO addSinger(SingerParams singerParams) {
//        先将singerParams转为实体类
        Singer singer = Singer.valueOf(singerParams);
//        将实体类插入数据库中，如果失败，抛异常
        if (!this.save(singer))
            throw new ServiceException(ResultCode.SINGER_SAVE_FAIL);
//        如果成功，初始化参数，转为MusicListInfoVO并返回
        singer.setMusicNums(0);
        singer.setAlbumNums(0);
        singer.setFansNums(0);
        return SingerInfoVO.valueOf(singer);
    }

    /***
     *  删除一个歌手
     * @param singerId
     * @return: SingerInfoVO
     */
    @Override
    public SingerInfoVO delSinger(Long singerId) {
        Singer singer = this.getById(singerId);
//        如果查不到，直接抛异常
        if (singer == null)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        将删除标记至为true，并更新至数据库中，失败，抛异常
        singer.setDeleteFlag(true);
        if (!updateById(singer))
            throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
//        将redsi中的singer对象直接删除
        if (redisTemplate.hasKey(MUSIC_SINGER + singerId)) {
            if (!redisTemplate.delete(MUSIC_SINGER + singerId))
                throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
        }
        if (redisTemplate.hasKey(MUSIC_SINGER + singerId + "AllMusic")) {
            if (!redisTemplate.delete(MUSIC_SINGER + singerId + "AllMusic"))
                throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
        }
        if (redisTemplate.hasKey(MUSIC_SINGER + singerId + "Album")) {
            if (!redisTemplate.delete(MUSIC_SINGER + singerId + "Album"))
                throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
        }
//        如果成功，转为MusicListInfoVO并返回
        return SingerInfoVO.valueOf(singer);
    }

    /***
     * 修改歌手信息
     * @param singerId
     * @param singerParams
     * @return:SingerInfoVO
     */
    @Override
    public SingerInfoVO updataSinger(Long singerId, SingerParams singerParams) {
//        根据歌手id得到歌手实体类
        Singer singer = this.getById(singerId);
//        如果查不到，直接抛异常
        if (singer == null)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        如果删除标记为true，直接抛异常
        if (singer.getDeleteFlag() == true)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        将实体类同步至SingerParams，并更新至数据库中
        singer.setId(singerId);
        singer.setName(singerParams.getName());
        singer.setSex(singerParams.getSex());
        singer.setArea(singerParams.getArea());
        singer.setCoverUrl(singerParams.getCoverUrl());
        singer.setGenres(singerParams.getGenres());
        singer.setDescription(singerParams.getDescription());
        if (!updateById(singer))
            throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
//        查看是否在redis中
        if (redisTemplate.hasKey(MUSIC_SINGER + singerId)) {
//        如果在redis中, 先将将redsi中的singer对象删除
            if (!redisTemplate.delete(MUSIC_SINGER + singerId))
                throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
//        再将新的对象放进redis中
            redisTemplate.opsForValue().set(MUSIC_SINGER + singerId, singer, 60 * 60, TimeUnit.SECONDS);
        }
        if (redisTemplate.hasKey(MUSIC_SINGER + singerId + "AllMusic")) {
            if (!redisTemplate.delete(MUSIC_SINGER + singerId + "AllMusic"))
                throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
        }
        if (redisTemplate.hasKey(MUSIC_SINGER + singerId + "Album")) {
            if (!redisTemplate.delete(MUSIC_SINGER + singerId + "Album"))
                throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
        }
//        如果成功，转为SingerInfoVO并返回
        return SingerInfoVO.valueOf(singer);
    }

    /***
     *  添加歌曲到歌手
     * @param singerMusicList
     * @return:SingerInfoVO
     */
    @Override
    public SingerInfoVO addMusicToSinger(List<SingerMusic> singerMusicList) {
//        将之前的redis缓存直接删掉即可
        if (redisTemplate.hasKey(MUSIC_SINGER + singerMusicList.get(0).getSingerId() + "AllMusic")) {
            redisTemplate.delete(MUSIC_SINGER + singerMusicList.get(0).getSingerId() + "AllMusic");
        }
//        根据歌手id获取歌手实体类
        Singer singer = this.getById(singerMusicList.get(0).getSingerId());
//        如果查不到，直接抛异常
        if (singer == null)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        直接将整个歌曲list添加至关系表中
        int addSuccessNums = 0;
        for (int i = 0; i < singerMusicList.size(); i++) {
            int isAddSuccess = singerMusicMapper.
                    insert(singerMusicList.get(i));
            if (isAddSuccess == 1) addSuccessNums++;
        }
//        歌单实体类的歌曲数量加上新增的歌曲数量
        singer.setMusicNums(singer.getMusicNums() + singerMusicList.size());
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(singer))
            throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
//        如果成功，转为SingerInfoVO并返回
        return SingerInfoVO.valueOf(singer);
    }

    /***
     *  删除歌手的歌曲
     * @param singerMusicList
     * @return:SingerInfoVO
     */
    @Override
    public SingerInfoVO delMusicToSinger(List<SingerMusic> singerMusicList) {
//        将之前的redis缓存直接删掉即可
        if (redisTemplate.hasKey(MUSIC_SINGER + singerMusicList.get(0).getSingerId() + "AllMusic")) {
            redisTemplate.delete(MUSIC_SINGER + singerMusicList.get(0).getSingerId() + "AllMusic");
        }
//        根据歌手id获取歌手实体类
        Singer singer = this.getById(singerMusicList.get(0).getSingerId());
//        如果查不到，直接抛异常
        if (singer == null)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        直接将整个歌曲list在关系表中删除
        Long singerId = singerMusicList.get(0).getSingerId();
        int delSuccessNums = 0;
        for (int i = 0; i < singerMusicList.size(); i++) {
            Map<String, Object> columnMap = new HashMap<>();
            columnMap.put("singer_id", singerId);
            columnMap.put("music_id", singerMusicList.get(i).getMusicId());
            int delSuccess = singerMusicMapper.deleteByMap(columnMap);
            if (delSuccess == 1) delSuccessNums++;
        }
//        歌单实体类的歌曲数量减去删除的歌曲数量
        singer.setMusicNums(singer.getMusicNums() - delSuccessNums);
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(singer))
            throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
//        如果成功，转为MusicListInfoVO并返回
        return SingerInfoVO.valueOf(singer);
    }

    /***
     *  添加专辑到歌手
     * @param singerAlbumList
     * @return:
     */
    @Override
    public SingerInfoVO addAlbumToSinger(List<SingerAlbum> singerAlbumList) {
//        将之前的redis缓存直接删掉即可
        if (redisTemplate.hasKey(MUSIC_SINGER + singerAlbumList.get(0).getSingerId() + "AllAlbum")) {
            redisTemplate.delete(MUSIC_SINGER + singerAlbumList.get(0).getSingerId() + "AllAlbum");
        }
//        根据歌手id获取歌手实体类
        Singer singer = this.getById(singerAlbumList.get(0).getSingerId());
//        如果查不到，直接抛异常
        if (singer == null)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        直接将整个专辑list添加至关系表中
        int addSuccessNums = 0;
        for (int i = 0; i < singerAlbumList.size(); i++) {
            int isAddSuccess = singerAlbumMapper.
                    insert(singerAlbumList.get(i));
            if (isAddSuccess == 1) addSuccessNums++;
        }
//        歌手实体类的专辑数量加上新增的专辑数量
        singer.setAlbumNums(singer.getAlbumNums() + singerAlbumList.size());
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(singer))
            throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
//        如果成功，转为SingerInfoVO并返回
        return SingerInfoVO.valueOf(singer);
    }

    /***
     *  删除歌手中的专辑
     * @param singerAlbumList
     * @return:
     */
    @Override
    public SingerInfoVO delAlbumToSinger(List<SingerAlbum> singerAlbumList) {
//        将之前的redis缓存直接删掉即可
        if (redisTemplate.hasKey(MUSIC_SINGER + singerAlbumList.get(0).getSingerId() + "AllAlbum")) {
            redisTemplate.delete(MUSIC_SINGER + singerAlbumList.get(0).getSingerId() + "AllAlbum");
        }
//        根据歌手id获取歌手实体类
        Singer singer = this.getById(singerAlbumList.get(0).getSingerId());
//        如果查不到，直接抛异常
        if (singer == null)
            throw new ServiceException(ResultCode.SINGER_GET_FAIL);
//        直接将整个歌曲list在关系表中删除
        Long singerId = singerAlbumList.get(0).getSingerId();
        int delSuccessNums = 0;
        for (int i = 0; i < singerAlbumList.size(); i++) {
            Map<String, Object> columnMap = new HashMap<>();
            columnMap.put("singer_id", singerId);
            columnMap.put("album_id", singerAlbumList.get(i).getAlbumId());
            int delSuccess = singerAlbumMapper.deleteByMap(columnMap);
            if (delSuccess == 1) delSuccessNums++;
        }
//        歌单实体类的歌曲数量减去删除的歌曲数量
        singer.setAlbumNums(singer.getAlbumNums() - delSuccessNums);
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(singer))
            throw new ServiceException(ResultCode.SINGER_UPDATE_FAIL);
//        如果成功，转为MusicListInfoVO并返回
        return SingerInfoVO.valueOf(singer);
    }

    /**
     * 随机获取redis中的前100粉丝数的歌
     */
    @Override
    public List<SingerInfoVO> getRandomSingerFromTop100(int count) {
        try {
            if (redisTemplate.hasKey(MUSIC_SINGER + "top100Singer")) {
                Set<String> randomSingerJsonSet = redisTemplate.opsForSet().distinctRandomMembers(MUSIC_SINGER + "top100Singer", count);
                List<SingerInfoVO> randomSingerList = new ArrayList<>();
                ObjectMapper objectMapper = new ObjectMapper();
                for (String singerJson : randomSingerJsonSet) {
                    SingerInfoVO singerInfoVO = objectMapper.readValue(singerJson, SingerInfoVO.class);
                    randomSingerList.add(singerInfoVO);
                }
                return randomSingerList;
            } else {
                addTop100Toredis();
                return getRandomSingerFromTop100(count);
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new ServiceException(ResultCode.MUSIC_TOP10000_FAIL);
        }
    }

    /**
     * 获取粉丝数前100的歌手,存入redis中
     */
    public void addTop100Toredis() throws JsonProcessingException {
        List<Singer> top100Singer = this.baseMapper.getTop100();
        for (Singer singer : top100Singer) {
            SingerInfoVO singerInfoVO = SingerInfoVO.valueOf(singer);
            String singerJson = new ObjectMapper().writeValueAsString(singerInfoVO);
            redisTemplate.opsForSet().add(MUSIC_SINGER + "top100Singer", singerJson);
        }
    }

}
